package business.histocloud;

import Jama.SingularValueDecomposition;
import business.histocloud.component.DrawBox;
import business.histocloud.component.Word;
import business.histocloud.algorithm.mds.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author thong
 */
public class LatentSemantic {

    List<Word> words1;
    List<Word> words2;
    List<Word> words3;
    String name1;
    String name2;
    String name3;
    List<String> allWords;
    List<DrawBox> allCommonWords;
    DrawBox[] journaux;

    //input matrix for SVD
    double[][] ar;
    
    //US = U*S, used in SVD
    double[][] US;
    //SV=S*V, used in SVD
    double[][] SV;

    public LatentSemantic(List<Word> words1, List<Word> words2, List<Word> words3, String name1, String name2, String name3) {
        this.words1 = words1;
        this.words2 = words2;
        this.words3 = words3;
        this.name1 = name1;
        this.name2 = name2;
        this.name3 = name3;

        allWords = new LinkedList<>();
        for (Word w : words1) {
            if (!allWords.contains(w.word)) {
                allWords.add(w.word);
            }
        }
        for (Word w : words2) {
            if (!allWords.contains(w.word)) {
                allWords.add(w.word);
            }
        }
        for (Word w : words3) {
            if (!allWords.contains(w.word)) {
                allWords.add(w.word);
            }
        }

        allCommonWords = new LinkedList<>();
        for (String w : allWords) {
            int count = 0;
            if (getWordFrequence(w, words1) > 0) {
                count++;
            }
            if (getWordFrequence(w, words2) > 0) {
                count++;
            }
            if (getWordFrequence(w, words3) > 0) {
                count++;
            }
            if (count >= 2) {
                DrawBox b = new DrawBox();
                b.word = w;
                allCommonWords.add(b);
            }
        }

        ar = new double[allCommonWords.size()][4];
        for (int i = 0; i < allCommonWords.size(); i++) {
            String word = allCommonWords.get(i).word;
            ar[i][0] = getWordFrequence(word, words1);
            ar[i][1] = getWordFrequence(word, words2);
            ar[i][2] = getWordFrequence(word, words3);
            ar[i][3] = getWordFrequence(word, words3);
            System.out.println(word);
        }

        Jama.Matrix matrixWord = new Jama.Matrix(ar);
        SingularValueDecomposition s = matrixWord.svd();

        //construct the necessary matrixes
        Jama.Matrix U = s.getU();
        Jama.Matrix S = s.getS();
        Jama.Matrix V = s.getV();        

        US = Matrix.multiply(U.getArray(), S.getArray());
        SV = Matrix.multiply(S.getArray(), V.getArray());

        //get poisitions
        for (int i = 0; i < allCommonWords.size(); i++) {
            DrawBox b = allCommonWords.get(i);
            b.rect.x = (int) US[i][0];
            b.rect.y = (int) US[i][1];
        }

        //create object to store the words and their positions
        journaux = new DrawBox[3];
        journaux[0] = new DrawBox();
        journaux[0].word = name1;
        journaux[0].rect.x = (int) SV[0][0];
        journaux[0].rect.y = (int) SV[1][0];

        journaux[1] = new DrawBox();
        journaux[1].word = name2;
        journaux[1].rect.x = (int) SV[0][1];
        journaux[1].rect.y = (int) SV[1][1];

        journaux[2] = new DrawBox();
        journaux[2].word = name3;
        journaux[2].rect.x = (int) SV[0][2];
        journaux[2].rect.y = (int) SV[1][2];
    }

    
    final int MAX_WIDTH = 400;
    final int MAX_HEIGHT = 400;
    final int v_gap = 50;
    final int h_gap = 50;

    
    //===================================PLAN=======================
    public BufferedImage getPlanImage() {
        Font font = new Font("Tahoma", Font.BOLD, 20);
        //add three journaux into list
        for (DrawBox b : journaux) {
            b.font = font;
            if (b.word.equals(name1))
            {
                b.color=Color.RED;
            }
            if (b.word.equals(name2))
            {
                b.color=Color.GREEN;
            }
            if (b.word.equals(name3))
            {
                b.color=Color.BLUE;
            }
            
            allCommonWords.add(b);
        }

        //repositioning within frame
        double minX = allCommonWords.get(0).rect.x;
        double maxX = allCommonWords.get(0).rect.x;
        double minY = allCommonWords.get(0).rect.y;
        double maxY = allCommonWords.get(0).rect.y;
        for (DrawBox b : allCommonWords) {
            if (b.rect.x > maxX) {
                maxX = b.rect.x;
            }
            if (b.rect.y > maxY) {
                maxY = b.rect.y;
            }
            if (b.rect.x < minX) {
                minX = b.rect.x;
            }
            if (b.rect.y < minY) {
                minY = b.rect.y;
            }
        }

        double ratiox;
        double ratioy;
        {
            double deltaX = maxX - minX;
            double deltaY = maxY - minY;

//            if (MAX_WIDTH / deltaX > MAX_HEIGHT / deltaY) {
//                ratio = MAX_HEIGHT / deltaY;
//            } else {
//                ratio = MAX_WIDTH / deltaX;
//            }
            
            //resize the iamge without keeping ratio scale
            ratiox=MAX_WIDTH/deltaX;
            ratioy=MAX_HEIGHT/deltaY;
        }

        for (DrawBox b : allCommonWords) {
            b.rect.x *= ratiox;
            b.rect.y *= ratioy;
            b.rect.x-=(int)(minX*ratiox);
            b.rect.y-=(int)(minY*ratioy);
        }

        BufferedImage image = new BufferedImage(MAX_WIDTH + 2 * v_gap, MAX_HEIGHT + 2 * h_gap, BufferedImage.TYPE_INT_ARGB);
        Graphics g = image.getGraphics();
        g.setColor(new Color(0.5f, 0.5f, 0.5f, 0.2f));
        g.fillRect(0, 0, image.getWidth(), image.getHeight());
        for (DrawBox b : allCommonWords) {
            g.setFont(b.font);
            g.setColor(b.color);           
            g.drawString(b.word, b.rect.x + v_gap, b.rect.y + h_gap + b.font.getSize());
        }
        return image;
    }

    //==============================================ISOMETRIC===========================
    final int v_margin = 50;
    final int h_margin = 100;
    final int pivot_height = 300;
    final double COS30 = Math.sqrt(3) / 2;
    final double SIN30 = 0.5;
    final int jitter = 5;

    public BufferedImage getIsometricImage() {
        BufferedImage bufferedImage = new BufferedImage((int) (2 * (h_margin + pivot_height * COS30)), (int) (2 * (v_margin + pivot_height)), BufferedImage.TYPE_INT_ARGB);
        Point center = new Point(bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
        //draw polygon
        Point[] points = new Point[7];
        points[0] = new Point(center.x, center.y);
        points[1] = new Point(points[0].x, points[0].x - pivot_height);
        points[2] = new Point(points[1].x + (int) (pivot_height * COS30), points[1].y + (int) (pivot_height * SIN30));
        points[3] = new Point(points[2].x, points[2].y + pivot_height);
        points[4] = new Point(center.x, center.y + pivot_height);
        points[5] = new Point(center.x - (int) (pivot_height * COS30), points[3].y);
        points[6] = new Point(center.x - (int) (pivot_height * COS30), points[2].y);

        Graphics g = bufferedImage.getGraphics();

        g.setColor(new Color(0.5f, 0.5f, 0.5f, 0.2f));
        int[] xPoints = new int[6];
        int[] yPoints = new int[6];
        for (int i = 0; i < 6; i++) {
            xPoints[i] = points[i + 1].x;
            yPoints[i] = points[i + 1].y;
        }
        g.fillPolygon(xPoints, yPoints, 6);

        g.setColor(Color.BLACK);
        g.drawLine(center.x, center.y, (int) (center.x + pivot_height * COS30), (int) (center.x + pivot_height * SIN30));
        g.drawLine(center.x, center.y, (int) (center.x - pivot_height * COS30), (int) (center.x + pivot_height * SIN30));
        g.drawLine(center.x, center.y, center.x, center.x - pivot_height);

        //draw names of journal     
        g.setFont(new Font("Tahoma", Font.BOLD, 30));
        g.setColor(Color.RED);
        g.drawString(name1, center.x + (int) (-pivot_height * COS30) - g.getFontMetrics().stringWidth(name1) / 2, center.y - (int) (pivot_height * SIN30));
        g.setColor(Color.GREEN);
        g.drawString(name2, center.x - g.getFontMetrics().stringWidth(name2) / 2, center.y + pivot_height);
        g.setColor(Color.BLUE);
        g.drawString(name3, (int) (center.x + pivot_height * COS30) - g.getFontMetrics().stringWidth(name3) / 2, (int) (center.y - pivot_height * SIN30));

        double maxFrequence = 0;
        allWords = new LinkedList<>();
        for (Word w : words1) {
            if (!allWords.contains(w.word)) {
                allWords.add(w.word);
            }
            if (maxFrequence < w.frequency) {
                maxFrequence = w.frequency;
            }
        }
        for (Word w : words2) {
            if (!allWords.contains(w.word)) {
                allWords.add(w.word);
            }
            maxFrequence = w.frequency;
        }
        for (Word w : words3) {
            if (!allWords.contains(w.word)) {
                allWords.add(w.word);
            }
            maxFrequence = w.frequency;
        }

        //construct vectors
        List<double[]> vectors = new LinkedList<>();
        for (int i=0;i<allCommonWords.size();i++) {
            double[] vector = new double[3];
            vector[0] = US[i][0];
            vector[1] = US[i][1];
            vector[2] = US[i][2];
            //proceeed vector
            double a = Math.sqrt(vector[1] * vector[1] + vector[2] * vector[2]) * pivot_height / 2 / maxFrequence;
            double b = Math.sqrt(vector[0] * vector[0] + vector[2] * vector[2]) * pivot_height / 2 / maxFrequence;
            double c = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]) * pivot_height / 2 / maxFrequence;
            vector[0] = a;
            vector[1] = b;
            vector[2] = c;
            vectors.add(vector);
        }
        
        //draw words and its position on the plan  
        g.setFont(new Font("Tahoma", Font.PLAIN, 20));
        for (int i = 0; i < vectors.size(); i++) {
            Point p = convertTo2D(vectors.get(i));
            //center p
            p.x = center.x + p.x;
            p.y = center.y - p.y;

            String w = allWords.get(i);
            g.setColor(Color.PINK);           
            g.setColor(Color.BLACK);
            g.drawString(w, p.x, p.y);
        }
        
        return bufferedImage;
    }

    public double getX2Statistic() {
        double result = 0;
        double total = 0;

        double[] totalFrequence = new double[3];
        for (String w : allWords) {
            Word word;
            word = getWordIn(w, words1);
            total += (word == null) ? 0 : word.frequency;
            word = getWordIn(w, words2);
            total += (word == null) ? 0 : word.frequency;
            word = getWordIn(w, words3);
            total += (word == null) ? 0 : word.frequency;
        }

        totalFrequence[0] = 0;
        for (Word w : words1) {
            totalFrequence[0] += w.frequency;
        }
        totalFrequence[1] = 0;
        for (Word w : words2) {
            totalFrequence[1] += w.frequency;
        }
        totalFrequence[2] = 0;
        for (Word w : words3) {
            totalFrequence[2] += w.frequency;
        }
        total = totalFrequence[0] + totalFrequence[1] + totalFrequence[2];
        for (String w : allWords) {
            Word word;
            double nominator;
            word = getWordIn(w, words1);
            double nominator1 = (word == null) ? 0 : word.frequency;
            word = getWordIn(w, words2);
            double nominator2 = (word == null) ? 0 : word.frequency;
            word = getWordIn(w, words3);
            double nominator3 = (word == null) ? 0 : word.frequency;
            nominator = nominator1 + nominator2 + nominator3;
            double expected1 = totalFrequence[0] * nominator / total;
            double expected2 = totalFrequence[1] * nominator / total;
            double expected3 = totalFrequence[2] * nominator / total;
            result += sqr(nominator1 - expected1) / expected1 + sqr(nominator2 - expected2) / expected2 + sqr(nominator3 - expected3) / expected3;
        }
        return result;
    }

    private double sqr(double value) {
        return value * value;
    }

    //convert a 3D vector to 2D vector to display on an isometric plan
    private Point convertTo2D(double[] vector) {
        Point p = new Point();
        p.x = (int) ((vector[0] - vector[2]) * COS30);
        p.y = (int) (vector[1] - (vector[0] + vector[2]) * SIN30);
        p.x += getJitter();
        p.y += getJitter();
        return p;
    }
    
    private Word getWordIn(String word, List<Word> list) {
        for (Word w : list) {
            if (w.word.equals(word)) {
                return w;
            }
        }
        return null;
    }
    
    private double getWordFrequence(String word, List<Word> list)
    {
        Word w = getWordIn(word, list);
        return w!=null? w.frequency : 0;
    }

    private int getJitter() {
        int sign = (int) (Math.random() * 20) % 2 == 0 ? 1 : -1;
        return sign * ((int) (Math.random() * jitter));
    }

}
